﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MobMentality.Social.People;
using Microsoft.Xna.Framework.Content;
using MobMentality.Network;

namespace MobMentality.Social
{
	public class PeopleHolder
	{
		private ContentManager content;
        private List<Person> people;
		private Random rand;

		//SINGLETON WOOO
		static PeopleHolder holder;

		public static PeopleHolder Instance
		{
			get
			{
				if(holder == null)
					holder = new PeopleHolder();
				return holder;
			}
		}

		private PeopleHolder()
		{
			/*Initialize stuff*/
			people = new List<Person>();
			rand = new Random();
		}

		public void Initialize(ContentManager content)
		{
			this.content = content;
		}

		/*spawn people on the map, add them to the given group and place them at the given start position*/
		public void SpawnPeople(int numPeople, IGroupController group, Vector2 position)
		{
			for (int i = 0; i < numPeople; i++)
			{
				int type = rand.Next(0, 11);
				GroupController controller = (GroupController)group;
				Person newMember;
				if (type >= 0 && type < 6)
				{
					newMember = new Melee(controller, position, content);
				}
				else if(type >= 6 && type < /*9*/11)
				{
					newMember = new Range(controller, position, content);
				}
                //temporarily took away healers for the sake of demo
				/* else if (type >= 9 && type < 11)
				{
					newMember = new Healer(controller, position, content);
				} */
				else
				{
					newMember = new Neutral(controller, position, content);
				}
				people.Add(newMember);
				controller.DirectAddMember(newMember);
			}
		}

		/*Directly add a created person*/
		internal void AddPerson(Person newMember)
		{
			people.Add(newMember);
		}

		/*return person at map location
		 * if selection radius is set, it will select person
		 * within that radius from mapLocation
		 */
		private Person PersonAtLocation(Vector2 mapLocation, float selectionRadius = 0.001f)
		{
			foreach (Person p in people)
			{
				float distanceToSelect = (p.MapPosition - mapLocation).Length();

				if (distanceToSelect <= selectionRadius)
				{
					return p;
				}
			}

			return null;
		}

		/*
		 * return all people within the given circle
		 */
		internal List<Person> PeopleInArea(Vector2 center, float radius = 0.001f)
		{
			List<Person> peopleInArea = new List<Person>();

			foreach (Person person in people)
			{
				Vector2 distance = person.MapPosition - center;
				if (distance.Length() < radius)
				{
					peopleInArea.Add(person);
				}
			}

			return peopleInArea;
		}

		/*
		 * return the nearest unconverted populace to the given vector
		 */
		internal Person NearestUnconverted(Vector2 mapPosition, PersonType type)
		{
			List<Person> unconvertedPeople = people.FindAll(p => (p.Group.GroupName == "Unconverted" && p.Type == type));

			if (unconvertedPeople.Count() <= 0)
			{
				return null;
			}

			unconvertedPeople.Sort(delegate(Person p1, Person p2)
						{
							//sort based on who's closest based on AStar
							int p1Dist = PersonNavigator.Instance.estimateMapDistance(mapPosition, p1.MapPosition);
							int p2Dist = PersonNavigator.Instance.estimateMapDistance(mapPosition, p2.MapPosition);

							if (p1Dist == p2Dist)
							{
								return (int)((p1.MapPosition - mapPosition).Length() - (p2.MapPosition - mapPosition).Length());
							}

							return p1Dist - p2Dist;
						});

			return unconvertedPeople[0];
		}

		/*
		 * return the group of the selected person
		 */
		public IGroupController SelectGroup(Vector2 mapLocation, float selectionRadius = 0.001f)
		{
			Person selectedPerson = PersonAtLocation(mapLocation, selectionRadius);
			if(selectedPerson == null)
			{
				return null;
			}
			else
			{
				return selectedPerson.Group;
			}
		}

		/*
		 * Draws all of the people (provided they are onscreen)
		 * 
		 * NOTE: Image is just here till we get person.draw, it's not good!
		 */
		public void Draw(Vector2 Camera, SpriteBatch spriteBatch)
		{
			foreach(Person p in people)
			{
				if(p.MapPosition.X > Camera.X && p.MapPosition.Y > Camera.Y)
				{
					p.Draw(Camera, spriteBatch);
				}
			}
		}

		/*
		 * Updates all of the people
		 */
		public void UpdatePeople()
		{
			//no dead people allowed
			people.RemoveAll(p => p.Health <= 0);

			foreach (Person p in people)
			{
				p.Update();
				PersonNavigator.Instance.movePerson(p);
			}
		}

		public bool CheckChaosEndgame()
		{
			if(people.Count(p => p.Team !=  Team.Riot) == 0)
			{
				return true;
			}

			return false;
		}

		public bool CheckSupressedEndgame()
		{
			if (people.Count(p => p.Team == Team.Riot) == 0)
			{
				return true;
			}

			return false;
		}

        /**
         * Gives a list of people in Vector4
         * Person Type, Person Group, Group Color and Team, Map Position
         */
        public List<Vector4> GenerateVectorList()
        {
            List<Vector4> peopleMessage = new List<Vector4>();
            foreach (Person person in people)
            {
                //NetOutgoingMessage personMessage = server.CreateMessage();
                /*Things to be sent: 
                    * 
                    * person.Type;
                person.MapPosition;
                person.Group.Team;
                person.Group.GroupColor;
                    */

                float hack = 0;
                if (person.Group.Team == Team.Police) hack = -1;
                else if (person.Group.Team == Team.Riot) hack = 1;
                hack *= person.Group.GroupColor.PackedValue;

                peopleMessage.Add(new Vector4((float)person.Type, hack, person.MapPosition.X, person.MapPosition.Y));
            }
            return peopleMessage;
        }


        //Updates nearby person's location based on vector attributes passed in
        //Used for network updating
        public void ChangePersonFromVector(Vector4 vperson)
        {
            List<Person> current = PeopleInArea(new Vector2(vperson.Y, vperson.Z), 10.0f);
            foreach (Person p in current) 
            {
                //if (p.Type == (PersonType)vperson.W)
                //{
                    if (vperson.X > 0)
                    {
                        //riot
                        if (p.Group.GroupColor.PackedValue == vperson.X)
                        {
                            p.SetTargetLocation(new Vector2(vperson.Y, vperson.Z));
                        }
                    }
                    else
                    {
                        //police
                        if (p.Group.GroupColor.PackedValue == -vperson.X)
                        {
                            p.SetTargetLocation(new Vector2(vperson.Y, vperson.Z));
                        }
                    }
                        
                //}
            }   
        }

		internal List<Person> EnemiesNearby(Vector2 mapPosition, Team myTeam, float radius)
		{
			List<Person> result = people.FindAll(p => (p.Team != Team.Unconverted && p.Team != myTeam && (p.MapPosition-mapPosition).Length() < radius));
			result.Sort(delegate(Person p1, Person p2)
			{ 
				int diff = (int)((mapPosition - p1.MapPosition).Length() - (mapPosition - p2.MapPosition).Length());
				return diff;
			});
			return result;
		}
	}
}
